package com.baijia.commons.authentication;

import com.baijia.commons.config.PassportClientConfig;
import com.baijia.commons.constant.Constant;
import com.baijia.commons.session.AbstractPassportFilter;
import com.baijia.commons.util.PPCommonUtils;

import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;

import org.apache.commons.lang3.StringUtils;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @title AuthenticationFilter
 * @desc 校验用户是否登录
 * @author caoliang
 * @date 2015年12月1日
 * @version 1.0
 */
public class AuthenticationFilter extends AbstractPassportFilter {

    /** Passport 登录请求url */
    private String casServerLoginUrl;

    /** 是否每次都发送request去做身份验证，即使客户端已有assertion和共享session */
    private boolean renew = false;

    /** 只要客户端有assertion和共享session，就不去校验身份；和renew正好相反，只能有一个生效 */
    private boolean gateway = false;

    @Override
    protected void initInternal(final FilterConfig filterConfig) throws ServletException {
        if (!isIgnoreInitConfiguration()) {
            super.initInternal(filterConfig);
            setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
            logger.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
            setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
            logger.trace("Loaded renew parameter: " + this.renew);
            setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
            logger.trace("Loaded gateway parameter: " + this.gateway);
        }
        setCasServerLoginUrl(passportConfig.getServerLogin());
    }

    @Override
    public void init() {
        super.init();
        PPCommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
    }

    private void sendPasscode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String mobile = request.getParameter(Constant.PRINCIPAL_ACCOUNT_MOBILE);
        JsonNode resp = null;
        if (StringUtils.isBlank(mobile)) {
            resp = new JsonNode("{\"code\":1, \"msg\":\"手机号不能为空！\"}");
        } else {
            try {
                resp =
                    Unirest.post(passportConfig.getSendPasscode()).field(Constant.PRINCIPAL_ACCOUNT_MOBILE, mobile)
                        .asJson().getBody();
            } catch (UnirestException e) {
                logger.error("Can't send passcode", e);
                resp = new JsonNode("{\"code\":1, \"msg\":\"系统异常，请稍后再试！\"}");
            }
        }
        response.setContentType("application/json; charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(resp.toString());
    }

    @Override
    public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
        final FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;

        final HttpSession session = request.getSession(false);
        final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_PASSPORT_ASSERTION) : null;
        final String path = request.getRequestURI().substring(request.getContextPath().length());
        logger.debug("Request path {}, assertion {}, session {}", path, assertion, session);

        if (StringUtils.endsWith(path, PassportClientConfig.SEND_PASSCODE_PATH)) {
            sendPasscode(request, response); // 发送验证码
            return;
        }

        // 兼容师资跳转,跳转过来时会带上tick，校验和当前用户是否一样，不一样重走doLogin逻辑
        final String ticket = PPCommonUtils.safeGetParameter(request, getArtifactParameterName());
        logger.info("pp_ticket :" + ticket);

        String tick = request.getParameter("tick");
        if (StringUtils.isBlank(tick)) {
            tick = request.getHeader("tick");
        }
        logger.info("tick :" + tick);

        if (assertion != null) {
            if (StringUtils.isNotBlank(tick)) {
                if (tick.equals(assertion.getAccountNumber().toString())) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("client-filter equals|tick:{},accountNumber:{}", tick,
                            assertion.getAccountNumber());
                    }
                    filterChain.doFilter(request, response);
                    return;
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("client-filter not equals|tick:{},accountNumber:{}", tick,
                            assertion.getAccountNumber());
                    }
                    if (StringUtils.isBlank(ticket)) {
                        // 清空session和cookie
                        session.invalidate();
                        Cookie cookie = new Cookie("_const_passport_id_", "");
                        cookie.setMaxAge(0);
                        cookie.setDomain(".genshuixue.com");
                        response.addCookie(cookie);

                        cookie = new Cookie("_const_dession_id", "");
                        cookie.setMaxAge(0);
                        cookie.setDomain(".genshuixue.com");
                        response.addCookie(cookie);
                    }
                }
            } else {
                filterChain.doFilter(request, response);
                return;
            }
        }

        if (PPCommonUtils.isNotBlank(ticket)) {
            filterChain.doFilter(request, response);
            return;
        }
        // else if(PPCommonUtils.isNotBlank(tgtId)){// cookie中TGT不为空 则通过TGT获取ST
        // final String serviceUrl = constructServiceUrl(request, response);
        // String gainStUrl = PPCommonUtils.constructRedirectUrl(passportConfig.getGainStUrl(),
        // getServiceParameterName(),
        // serviceUrl, this.appId);
        // if (logger.isDebugEnabled()) {
        // logger.debug("redirecting to \"" + gainStUrl + "\"");
        // }
        // response.addHeader("P3P", "CP=CAO PSA OUR");
        // response.sendRedirect(gainStUrl);
        // return;
        // }

        // 登录页面不能跳转
        if (request.getRequestURL().toString().equalsIgnoreCase(passportConfig.getClientLoginAddr())) {
            filterChain.doFilter(request, response);
            return;
        }

        logger.info("Redirect {}", passportConfig.getRedirect());
        if (passportConfig.getRedirect() == null || passportConfig.getRedirect() == true) {
            final String serviceUrl = constructServiceUrl(request, response);
            logger.info("serviceUrl :" + serviceUrl);
            String urlToRedirectTo =
                PPCommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), serviceUrl,
                    this.appId);

            if (logger.isDebugEnabled()) {
                logger.debug("redirecting to \"" + urlToRedirectTo + "\"");
            }
            response.addHeader("P3P", "CP=CAO PSA OUR");
            response.sendRedirect(urlToRedirectTo);
        } else {
            needLoginResponse(request, response);
        }

        return;
    }

    public final void setRenew(final boolean renew) {
        this.renew = renew;
    }

    public final void setGateway(final boolean gateway) {
        this.gateway = gateway;
    }

    public final void setCasServerLoginUrl(final String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public String retrieveCookieValue(final HttpServletRequest request) {
        final Cookie cookie = org.springframework.web.util.WebUtils.getCookie(request, CONST_PASSPORT_ID);

        return cookie == null ? null : cookie.getValue();
    }

}
